package robot.calibeta;

import chair.Behavior;
import chair.Brain;
import chair.Chronos;
import chair.Function;
import chair.Organ;
import chair.Reflex;
import chair.core.CoreNoisyOrModel;
import robot.Head;

public class HeadCalibeta extends Head {
	/** À quelle fonction a-t-on rattaché la motricité ? */
	Function _motionFunction;

	/**
	 * Ce constructeur permet de spécifier si on veut simplifier l'expérience
	 * (seulement deux direction : Nord/Sud, pas de rotations), ainsi que si on
	 * souhaite qu'OrganSomato entraîne l'extinction du programme
	 * 
	 * @param simple
	 *            true pour une version simplifiée, false pour la version
	 *            normale
	 * @param suicide
	 *            true si le programme se termine quand on soulève le robot,
	 *            false si OrganSomato n'est pas utilisé
	 * @param emergency
	 *            quelques heures avant le passage c'est le drame : capteur RFID
	 *            marche plus. Solution de secours avec capteur tactile
	 */
	public HeadCalibeta(boolean simple, boolean suicide, boolean emergency) {
		// On règle les paramètres de l'expérience
		Chronos.setModelDeltaMax(5000);
		Chronos.setModelStepTime(350);

		// Ici tous les comportements vont à leurs termes, réduit délais
		// possible entre deux BehaviorGoStraight
		Chronos.setBehaviorPollingTime(1000);

		// Initialisation du modèle
		_model = new CoreNoisyOrModel();
		// Paramètres modèle
		_model.setAlpha(0.4);
		_model.setForecastThreshold(0.4);

		// Robby devient un être à part entière
		_robby = new Brain(_model);

		// Si en situation d'urgence ce sera capteur tactile et non RFID (avec
		// donc seulement une sensation : fraise, punition impossible)
		Organ smell;
		if (!emergency)
			smell = new OrganSmell(_robby);
		else
			smell = new OrganSmellER(_robby);

		OrganEyeColor seeColor = new OrganEyeColor(_robby);

		// Classe propre à cette simulation
		Cartographer magellan;
		// version simple : que Nord/Sud
		if (simple)
			magellan = new Cartographer(seeColor, false);
		else
			magellan = new Cartographer(seeColor);

		// Organ particulier : pas de senseur du robot mais magellan qui met un
		// pied à terre.
		Organ orgMagellan = new OrganCartographer(_robby, magellan);

		// Initialisation des fonctions du robot : commence par déplacements
		_motionFunction = new Function(_robby, "Se mouvoir");
		// Qui est en train de lire la carte ?
		Function mapReader = new Function(_robby, "Itinéraire");
		// Sert juste pour réflexes qui ne vont pas entrer en compétition :
		// somatique, quitter programme (sauf si calicon) et voice
		Function dummy = new Function(_robby, "Pour rire");

		// Initialisation des réflexes du robot
		Reflex discoverJunction = new ReflexJunction(_motionFunction, magellan);
		Reflex returnKennel = new ReflexReturnKennel(mapReader, magellan);
		// 5 secondes permet en très gros trois interrogation du capteur RFID
		Reflex freeze = new ReflexFreeze(_motionFunction, 5000);
		// Réflexe de la plus haute importance : va créer les comportements
		// permettant de revenir dans une salle une fois celles-ci visitées
		Reflex addBehavior = new ReflexAddBehavior(dummy, mapReader, _robby,
				magellan);
		// Robot émet un son à chaque sensation de smell, peut montrer qu'il
		// prédit balises
		Reflex responseVoice = new ReflexVoice(dummy);

		// Initialisation du comportement du robot
		Behavior goStraight;
		// version simple : pas de zigzag
		if (simple)
			goStraight = new BehaviorGoStraight(_motionFunction, seeColor,
					magellan, 0);
		else
			goStraight = new BehaviorGoStraight(_motionFunction, seeColor,
					magellan);

		// Au début un seul comportement : explorer
		Behavior explore = new BehaviorExplore(mapReader, magellan);

		// On branche
		_robby.addBehavior(goStraight);
		_robby.addBehavior(explore);
		// On ne tourne que sur un marqueur rouge
		_robby.plug(seeColor, (byte) 2, discoverJunction);
		// Même réflexe gère aussi les salles
		_robby.plugDefault(seeColor, discoverJunction);
		// Et comme les nouvelle sensation seront de nouvelles salles : on les
		// veut pour ajouter comportements
		_robby.plugDefault(seeColor, addBehavior);

		// Si configuré pour, on ajoute interrupteur mortel au sein même du
		// robot
		if (suicide) {
			Reflex seppuku = new ReflexSeppuku(dummy);
			Organ somato = new OrganSomato(_robby);
			_robby.plug(somato, seppuku);
		}

		// Le robot est bavard quand on lui chatouille le nez
		_robby.plug(smell, responseVoice);

		// Robot attend signal RFID
		/*
		 * NB: on fait ici attention à ne pas brancher à sensation 1 car sinon
		 * pendant laps de temps avant returnKennel, peut initier un
		 * BehaviorGoStraight inutile. Robot attendra que ce dernier se termine
		 * pour en retenter un qui cette fois aura une direction à prendre.
		 * 
		 * FIXME: pour éviter délai de changement de comportement, le calcul de
		 * l'itinéraire doit être plus rapide que le temps d'immobilisation du
		 * robot;
		 * 
		 * TODO: mécanisme pour éviter ça, genre Reflex de type flush pour que
		 * fonction initie pas Behavior, ou que goStraight teste jusqu'à obtenir
		 * direction, ou je ne sais quoi.
		 */
		for (byte i = 2; i < 6; i++)
			_robby.plug(orgMagellan, i, freeze);

		// Retourne à la niche tout le temps...sauf quand il y est déjà.
		for (byte i = 3; i < 6; i++)
			_robby.plug(orgMagellan, i, returnKennel);

		// Version simple : recule au lieu de faire demi-tour
		if (simple)
			Motion.setRotateShortest(true);
		// Ici on a un peu plus que le package chair à gérer
		Motion.init();

		// Et on je mets le son !
		Voice.Melody.ALSO_SPRACH_ZARATHUSTRA.play();
	}

	/**
	 * Par défaut : version non simplifiée qui s'éteint dès qu'on soulève le
	 * robot
	 */
	public HeadCalibeta() {
		this(false, true, false);
	}

	/**
	 * Destinée à calicon : OrganCage a besoin de contrôler motricité
	 * 
	 * @return la fonction associé au contrôle des roues
	 */
	protected Function getMotionFunction() {
		return _motionFunction;
	}
}
